home *** CD-ROM | disk | FTP | other *** search
- /* general.c -- Stuff that is used by all files. */
-
- /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
-
- This file is part of GNU Bash, the Bourne Again SHell.
-
- Bash is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 1, or (at your option) any later
- version.
-
- Bash is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
-
- You should have received a copy of the GNU General Public License along
- with Bash; see the file COPYING. If not, write to the Free Software
- Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/param.h>
- #include "filecntl.h"
-
- #include "shell.h"
- #if defined (USG)
- #include <string.h>
- #else
- #include <strings.h>
- #endif /* USG */
-
- #if !defined (USG) || defined (HAVE_RESOURCE)
- #include <sys/time.h>
- #endif
-
- #include <sys/times.h>
- #include "maxpath.h"
-
- #ifndef NULL
- #define NULL 0x0
- #endif
-
- extern int errno;
-
- /* Make the functions index and rindex if they do not exist. */
- #if defined (USG) && !defined (sgi) && !defined (DGUX) && !defined (index)
- char *
- index (s, c)
- char *s;
- {
- return ((char *)strchr (s, c));
- }
- #endif /* USG && !sgi && !DGUX && !index */
-
- #if defined (USG) && !defined (sgi) && !DGUX && !defined (rindex)
- char *
- rindex (s, c)
- char *s;
- {
- return ((char *)strrchr (s, c));
- }
- #endif /* USG && !sgi && !DUGX && !rindex */
-
- /* **************************************************************** */
- /* */
- /* Memory Allocation and Deallocation. */
- /* */
- /* **************************************************************** */
-
- char *
- xmalloc (size)
- int size;
- {
- register char *temp = (char *)malloc (size);
-
- if (!temp)
- fatal_error ("Out of virtual memory!");
-
- return (temp);
- }
-
- char *
- xrealloc (pointer, size)
- register char *pointer;
- int size;
- {
- char *temp;
-
- if (!pointer)
- temp = (char *)xmalloc (size);
- else
- temp = (char *)realloc (pointer, size);
-
- if (!temp)
- fatal_error ("Out of virtual memory!");
-
- return (temp);
- }
-
-
- /* **************************************************************** */
- /* */
- /* Integer to String Conversion */
- /* */
- /* **************************************************************** */
-
- /* Number of characters that can appear in a string representation
- of an integer. 32 is larger than the string rep of 2^^31 - 1. */
- #define MAX_INT_LEN 32
-
- /* Integer to string conversion. This conses the string; the
- caller should free it. */
- char *
- itos (i)
- int i;
- {
- char *buf, *p, *ret;
- int negative = 0;
- unsigned int ui;
-
- buf = xmalloc (MAX_INT_LEN);
-
- if (i < 0)
- {
- negative++;
- i = -i;
- }
-
- ui = (unsigned int) i;
-
- buf[MAX_INT_LEN - 1] = '\0';
- p = &buf[MAX_INT_LEN - 2];
-
- do
- *p-- = (ui % 10) + '0';
- while (ui /= 10);
-
- if (negative)
- *p-- = '-';
-
- ret = savestring (p + 1);
- free (buf);
- return (ret);
- }
-
- /* Return non-zero if all of the characters in STRING are digits. */
- int
- all_digits (string)
- char *string;
- {
- while (*string)
- {
- if (!digit (*string))
- return (0);
- else
- string++;
- }
- return (1);
- }
-
- /* A function to unset no-delay mode on a file descriptor. Used in shell.c
- to unset it on the fd passed as stdin. Should be called on stdin if
- readline gets an EAGAIN or EWOULDBLOCK when trying to read input. */
-
- #if !defined (O_NDELAY)
- # if defined (FNDELAY)
- # define O_NDELAY FNDELAY
- # endif
- #endif /* O_NDELAY */
-
- /* Make sure no-delay mode is not set on file descriptor FD. */
- void
- unset_nodelay_mode (fd)
- int fd;
- {
- int flags, set = 0;
-
- if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
- return;
-
- #if defined (O_NONBLOCK)
- if (flags & O_NONBLOCK)
- {
- flags &= ~O_NONBLOCK;
- set++;
- }
- #endif /* O_NONBLOCK */
-
- #if defined (O_NDELAY)
- if (flags & O_NDELAY)
- {
- flags &= ~O_NDELAY;
- set++;
- }
- #endif /* O_NDELAY */
-
- if (set)
- fcntl (fd, F_SETFL, flags);
- }
-
-
- /* **************************************************************** */
- /* */
- /* Generic List Functions */
- /* */
- /* **************************************************************** */
-
- /* Call FUNCTION on every member of LIST, a generic list. */
- void
- map_over_list (list, function)
- GENERIC_LIST *list;
- Function *function;
- {
- while (list) {
- (*function) (list);
- list = list->next;
- }
- }
-
- /* Call FUNCTION on every string in WORDS. */
- void
- map_over_words (words, function)
- WORD_LIST *words;
- Function *function;
- {
- while (words) {
- (*function)(words->word->word);
- words = words->next;
- }
- }
-
- /* Reverse the chain of structures in LIST. Output the new head
- of the chain. You should always assign the output value of this
- function to something, or you will lose the chain. */
- GENERIC_LIST *
- reverse_list (list)
- register GENERIC_LIST *list;
- {
- register GENERIC_LIST *next, *prev = (GENERIC_LIST *)NULL;
-
- while (list) {
- next = list->next;
- list->next = prev;
- prev = list;
- list = next;
- }
- return (prev);
- }
-
- /* Return the number of elements in LIST, a generic list. */
- int
- list_length (list)
- register GENERIC_LIST *list;
- {
- register int i;
-
- for (i = 0; list; list = list->next, i++);
- return (i);
- }
-
- /* Delete the element of LIST which satisfies the predicate function COMPARER.
- Returns the element that was deleted, so you can dispose of it, or -1 if
- the element wasn't found. COMPARER is called with the list element and
- then ARG. Note that LIST contains the address of a variable which points
- to the list. You might call this function like this:
-
- SHELL_VAR *elt = delete_element (&variable_list, check_var_has_name, "foo");
- dispose_variable (elt);
- */
- GENERIC_LIST *
- delete_element (list, comparer, arg)
- GENERIC_LIST **list;
- Function *comparer;
- {
- register GENERIC_LIST *prev = (GENERIC_LIST *)NULL;
- register GENERIC_LIST *temp = *list;
-
- while (temp) {
- if ((*comparer) (temp, arg)) {
- if (prev) prev->next = temp->next;
- else *list = temp->next;
- return (temp);
- }
- prev = temp;
- temp = temp->next;
- }
- return ((GENERIC_LIST *)-1);
- }
-
- /* Find NAME in ARRAY. Return the index of NAME, or -1 if not present.
- ARRAY shoudl be NULL terminated. */
- int
- find_name_in_list (name, array)
- char *name, *array[];
- {
- int i;
-
- for (i=0; array[i]; i++)
- if (strcmp (name, array[i]) == 0)
- return (i);
-
- return (-1);
- }
-
- /* Return the length of ARRAY, a NULL terminated array of char *. */
- int
- array_len (array)
- register char **array;
- {
- register int i;
- for (i=0; array[i]; i++);
- return (i);
- }
-
- /* Free the contents of ARRAY, a NULL terminated array of char *. */
- void
- free_array (array)
- register char **array;
- {
- register int i = 0;
-
- if (!array) return;
-
- while (array[i])
- free (array[i++]);
- free (array);
- }
-
- /* Append LIST2 to LIST1. Return the header of the list. */
- GENERIC_LIST *
- list_append (head, tail)
- GENERIC_LIST *head, *tail;
- {
- register GENERIC_LIST *t_head = head;
-
- if (!t_head)
- return (tail);
-
- while (t_head->next) t_head = t_head->next;
- t_head->next = tail;
- return (head);
- }
-
- /* Some random string stuff. */
-
- /* Remove all leading whitespace from STRING. This includes
- newlines. STRING should be terminated with a zero. */
- void
- strip_leading (string)
- char *string;
- {
- char *start = string;
-
- while (*string && (whitespace (*string) || *string == '\n')) string++;
-
- if (string != start)
- {
- int len = strlen (string);
- bcopy (string, start, len);
- start[len] = '\0';
- }
- }
-
- /* Remove all trailing whitespace from STRING. This includes
- newlines. If NEWLINES_ONLY is non-zero, only trailing newlines
- are removed. STRING should be terminated with a zero. */
- void
- strip_trailing (string, newlines_only)
- char *string;
- int newlines_only;
- {
- int len = strlen (string) - 1;
-
- while (len >= 0)
- {
- if ((newlines_only && string[len] == '\n') ||
- (!newlines_only && whitespace (string[len])))
- len--;
- else
- break;
- }
- string[len + 1] = '\0';
- }
-
-
- /* Remove the last N directories from PATH. Do not PATH blank.
- PATH must contain enoung space for MAXPATHLEN characters. */
- static void
- pathname_backup (path, n)
- char *path;
- int n;
- {
- register char *p;
-
- if (!*path)
- return;
-
- p = path + (strlen (path) - 1);
-
- while (n--)
- {
- while (*p == '/' && p != path)
- p--;
-
- while (*p != '/' && p != path)
- p--;
-
- *++p = '\0';
- }
- }
-
- static char current_path[MAXPATHLEN];
-
- /* Turn STRING (a pathname) into an absolute pathname, assuming that
- DOT_PATH contains the symbolic location of '.'. This always
- returns a new string, even if STRING was an absolute pathname to
- begin with. */
- char *
- make_absolute (string, dot_path)
- char *string, *dot_path;
- {
- register char *cp;
-
- if (!dot_path || *string == '/')
- return (savestring (string));
-
- strcpy (current_path, dot_path);
-
- if (!current_path[0])
- strcpy (current_path, "./");
-
- cp = current_path + (strlen (current_path) - 1);
-
- if (*cp++ != '/')
- *cp++ = '/';
-
- *cp = '\0';
-
- while (*string)
- {
- if (*string == '.')
- {
- if (!string[1])
- return (savestring (current_path));
-
- if (string[1] == '/')
- {
- string += 2;
- continue;
- }
-
- if (string[1] == '.' && (string[2] == '/' || !string[2]))
- {
- string += 2;
-
- if (*string)
- string++;
-
- pathname_backup (current_path, 1);
- cp = current_path + strlen (current_path);
- continue;
- }
- }
-
- while (*string && *string != '/')
- *cp++ = *string++;
-
- if (*string)
- *cp++ = *string++;
-
- *cp = '\0';
- }
- return (savestring (current_path));
- }
-
- /* Return 1 if STRING contains an absolute pathname, else 0. */
- int
- absolute_pathname (string)
- char *string;
- {
- if (!string || !*string)
- return (0);
-
- if (*string == '/')
- return (1);
-
- if (*string++ == '.')
- {
- if ((!*string) || *string == '/')
- return (1);
-
- if (*string++ == '.')
- if (!*string || *string == '/')
- return (1);
- }
- return (0);
- }
-
- /* Return 1 if STRING is an absolute program name; it is absolute if it
- contains any slashes. This is used to decide whether or not to look
- up through $PATH. */
- int
- absolute_program (string)
- char *string;
- {
- return ((char *)index (string, '/') != (char *)NULL);
- }
-
- /* Return the `basename' of the pathname in STRING (the stuff after the
- last '/'). If STRING is not a full pathname, simply return it. */
- char *
- base_pathname (string)
- char *string;
- {
- char *p = (char *)rindex (string, '/');
-
- if (!absolute_pathname (string))
- return (string);
-
- if (p)
- return (++p);
- else
- return (string);
- }
-
- /* Determine if s2 occurs in s1. If so, return a pointer to the
- match in s1. The compare is case insensitive. */
- char *
- strindex (s1, s2)
- register char *s1, *s2;
- {
- register int i, l = strlen (s2);
- register int len = strlen (s1);
-
- for (i = 0; (len - i) >= l; i++)
- if (strnicmp (&s1[i], s2, l) == 0)
- return (s1 + i);
- return ((char *)NULL);
- }
-
- #if !defined (to_upper)
- #define lowercase_p(c) (((c) > ('a' - 1) && (c) < ('z' + 1)))
- #define uppercase_p(c) (((c) > ('A' - 1) && (c) < ('Z' + 1)))
- #define pure_alphabetic(c) (lowercase_p(c) || uppercase_p(c))
- #define to_upper(c) (lowercase_p(c) ? ((c) - 32) : (c))
- #define to_lower(c) (uppercase_p(c) ? ((c) + 32) : (c))
- #endif /* to_upper */
-
- /* Compare at most COUNT characters from string1 to string2. Case
- doesn't matter. */
- int
- strnicmp (string1, string2, count)
- char *string1, *string2;
- {
- register char ch1, ch2;
-
- while (count) {
- ch1 = *string1++;
- ch2 = *string2++;
- if (to_upper(ch1) == to_upper(ch2))
- count--;
- else break;
- }
- return (count);
- }
-
- /* strcmp (), but caseless. */
- int
- stricmp (string1, string2)
- char *string1, *string2;
- {
- register char ch1, ch2;
-
- while (*string1 && *string2) {
- ch1 = *string1++;
- ch2 = *string2++;
- if (to_upper(ch1) != to_upper(ch2))
- return (1);
- }
- return (*string1 | *string2);
- }
-
- /* Return a string corresponding to the error number E. From
- the ANSI C spec. */
- #if defined (strerror)
- #undef strerror
- #endif
-
- #if !defined (HAVE_STRERROR)
- char *
- strerror (e)
- int e;
- {
- extern int sys_nerr;
- extern char *sys_errlist[];
- static char emsg[40];
-
- if (e > 0 && e < sys_nerr)
- return (sys_errlist[e]);
- else
- {
- sprintf (emsg, "Unknown error %d", e);
- return (&emsg[0]);
- }
- }
- #endif /* HAVE_STRERROR */
-
- #if !defined (USG) || defined (HAVE_RESOURCE)
- /* Print the contents of a struct timeval * in a standard way. */
- void
- print_timeval (tvp)
- struct timeval *tvp;
- {
- int minutes, seconds_fraction;
- long seconds;
-
- seconds = tvp->tv_sec;
-
- seconds_fraction = tvp->tv_usec % 1000000;
- seconds_fraction = (seconds_fraction * 100) / 1000000;
-
- minutes = seconds / 60;
- seconds %= 60;
-
- printf ("%0dm%0d.%02ds", minutes, seconds, seconds_fraction);
- }
- #endif
-
- /* Print the time defined by a time_t (returned by the `times' and `time'
- system calls) in a standard way. This is scaled in terms of HZ, which
- is what is returned by the `times' call. */
-
- #if !defined (BrainDeath)
- # if !defined (HZ)
- # if defined (USG)
- # define HZ 100 /* From my Sys V.3.2 manual for times(2) */
- # else
- # define HZ 60 /* HZ is always 60 on BSD systems */
- # endif /* USG */
- # endif /* HZ */
-
- void
- print_time_in_hz (t)
- time_t t;
- {
- int minutes, seconds_fraction;
- long seconds;
-
- seconds_fraction = t % HZ;
- seconds_fraction = (seconds_fraction * 100) / HZ;
-
- seconds = t / HZ;
-
- minutes = seconds / 60;
- seconds %= 60;
-
- printf ("%0dm%0d.%02ds", minutes, seconds, seconds_fraction);
- }
- #endif /* BrainDeath */
-
- #if !defined (HAVE_DUP2)
- /* Replacement for dup2 (), for those systems which either don't have it,
- or supply one with broken behaviour. */
- int
- dup2 (fd1, fd2)
- int fd1, fd2;
- {
- int saved_errno, r;
-
- /* If FD1 is not a valid file descriptor, then return immediately with
- an error. */
- if (fcntl (fd1, F_GETFL, 0) == -1)
- return (-1);
-
- if (fd2 < 0 || fd2 >= getdtablesize ())
- {
- errno = EBADF;
- return (-1);
- }
-
- if (fd1 == fd2)
- return (0);
-
- saved_errno = errno;
-
- (void) close (fd2);
- r = fcntl (fd1, F_DUPFD, fd2);
-
- if (r >= 0)
- errno = saved_errno;
- else
- if (errno == EINVAL)
- errno = EBADF;
-
- /* Force the new file descriptor to remain open across exec () calls. */
- SET_OPEN_ON_EXEC (fd2);
- return (r);
- }
- #endif /* !HAVE_DUP2 */
-
- /*
- * Return the total number of available file descriptors.
- *
- * On some systems, like 4.2BSD and its descendents, there is a system call
- * that returns the size of the descriptor table: getdtablesize(). There are
- * lots of ways to emulate this on non-BSD systems.
- *
- * On System V.3, this can be obtained via a call to ulimit:
- * return (ulimit(4, 0L));
- *
- * On other System V systems, NOFILE is defined in /usr/include/sys/param.h
- * (this is what we assume below), so we can simply use it:
- * return (NOFILE);
- *
- * On POSIX systems, there are specific functions for retrieving various
- * configuration parameters:
- * return (sysconf(_SC_OPEN_MAX));
- *
- */
-
- #if defined (USG) || defined (HPUX)
- int
- getdtablesize ()
- {
- # if defined (_POSIX_VERSION) && defined (_SC_OPEN_MAX)
- return (sysconf(_SC_OPEN_MAX)); /* Posix systems use sysconf */
- # else /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
- # if defined (USGr3)
- return (ulimit (4, 0L)); /* System V.3 systems use ulimit(4, 0L) */
- # else /* !USGr3 */
- # if defined (NOFILE) /* Other systems use NOFILE */
- return (NOFILE);
- # else /* !NOFILE */
- return (20); /* XXX - traditional value is 20 */
- # endif /* !NOFILE */
- # endif /* !USGr3 */
- # endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
- }
- #endif /* USG && !defined USGr4 */
-
- #if defined (USG) && !defined (sgi)
-
- #if !defined (RISC6000)
- bcopy(s,d,n) char *d,*s; { memcpy (d, s, n); }
- bzero(s,n) char *s; int n; { memset(s, '\0', n); }
- #endif /* RISC6000 */
-
- #if !defined (HAVE_GETWD)
- char *
- getwd (string)
- char *string;
- {
- extern char *getcwd ();
- char *result;
-
- result = getcwd (string, MAXPATHLEN);
- if (result == NULL)
- strcpy (string, "getwd: cannot access parent directories");
- return (result);
- }
- #endif /* !HAVE_GETWD */
-
- #if !defined (HPUX)
- #include <sys/utsname.h>
- int
- gethostname (name, namelen)
- char *name;
- int namelen;
- {
- int i;
- struct utsname ut;
-
- --namelen;
-
- uname (&ut);
- i = strlen (ut.nodename) + 1;
- strncpy (name, ut.nodename, i < namelen ? i : namelen);
- name[namelen] = '\0';
- return (0);
- }
- #endif /* !HPUX */
- #endif /* USG && !sgi */
-
- /* A slightly related function. Get the prettiest name of this
- directory possible. */
- static char tdir[MAXPATHLEN];
-
- /* Return a pretty pathname. If the first part of the pathname is
- the same as $HOME, then replace that with `~'. */
- char *
- polite_directory_format (name)
- char *name;
- {
- char *home = get_string_value ("HOME");
- int l = home ? strlen (home) : 0;
-
- if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
- {
- strcpy (tdir + 1, name + l);
- tdir[0] = '~';
- return (tdir);
- }
- else
- return (name);
- }
-
- #if defined (USG) || (defined (_POSIX_VERSION) && defined (Ultrix))
- int
- sysv_getc (stream)
- FILE *stream;
- {
- int result;
- char c;
-
- while (1)
- {
- result = read (fileno (stream), &c, sizeof (char));
-
- if (result == 0)
- return (EOF);
-
- if (result == sizeof (char))
- return (c);
-
- if (errno != EINTR)
- return (EOF);
- }
- }
-
- /* USG and POSIX systems do not have killpg (). But we use it in
- jobs.c, nojobs.c and builtins.c. */
- #if !defined (_POSIX_VERSION)
- #define pid_t int
- #endif /* _POSIX_VERSION */
-
- int
- killpg (pgrp, sig)
- pid_t pgrp;
- int sig;
- {
- int result;
-
- result = kill (-pgrp, sig);
- return (result);
- }
- #endif /* USG || _POSIX_VERSION */
-
- /* **************************************************************** */
- /* */
- /* Tilde Initialization and Expansion */
- /* */
- /* **************************************************************** */
-
- /* If tilde_expand hasn't been able to expand the text, perhaps it
- is a special shell expansion. This function is installed as the
- tilde_expansion_failure_hook. It knows how to expand ~- and ~+. */
- static char *
- bash_tilde_expand (text)
- char *text;
- {
- char *result = (char *)NULL;
-
- if (strcmp (text, "-") == 0)
- result = get_string_value ("OLDPWD");
- else if (strcmp (text, "+") == 0)
- result = get_string_value ("PWD");
-
- if (result)
- result = savestring (result);
-
- return (result);
- }
-
- /* Initialize the tilde expander. In Bash, we handle `~-' and `~+', as
- well as handling special tilde prefixes; `:~" and `=~' are indications
- that we should do tilde expansion. */
- void
- tilde_initialize ()
- {
- extern Function *tilde_expansion_failure_hook;
- extern char **tilde_additional_prefixes, **tilde_additional_suffixes;
- static int times_called = 0;
-
- /* Tell the tilde expander that we want a crack if it fails. */
- tilde_expansion_failure_hook = (Function *)bash_tilde_expand;
-
- /* Tell the tilde expander about special strings which start a tilde
- expansion, and the special strings that end one. Only do this once.
- tilde_initialize () is called from within bashline_reinitialize (). */
- if (times_called == 0)
- {
- tilde_additional_prefixes = (char **)xmalloc (3 * sizeof (char *));
- tilde_additional_prefixes[0] = "=~";
- tilde_additional_prefixes[1] = ":~";
- tilde_additional_prefixes[2] = (char *)NULL;
-
- tilde_additional_suffixes = (char **)xmalloc (3 * sizeof (char *));
- tilde_additional_suffixes[0] = ":";
- tilde_additional_suffixes[1] = "=~";
- tilde_additional_suffixes[2] = (char *)NULL;
- }
- times_called++;
- }
-